package dwarffortress.io;

import java.io.InputStream;
import java.io.IOException;
import java.util.zip.Inflater;
import java.util.zip.DataFormatException;

public class DFInflaterStream extends InputStream {
    private InputStream source;
    Inflater inflater;

    public DFInflaterStream(InputStream source) {
        this.source = source;
    }

    /**
     * Returns size of next chunk, or -1 if EOF is reached.
     *
     * @return
     * @throws IOException
     */
    private int readNextSize() throws IOException {
        int result = 0;
        int k = source.read();
        if (k < 0) {
            return -1;
        }
        result = (k & 0xff);
        for (int i = 1; i < 4; i++) {
            k = source.read();
            if (k < 0) {
                throw new IOException("Can not read next chunk size : end of file");
            }
            result = result | ((k & 0xff) << i * 8);
        }
        return result;
    }


    public int read() throws IOException {
        if (!loadNextChunk())  {
            return -1;
        }
        byte[] buf = new byte[1];
        try {
            int read = inflater.inflate(buf);
            while (read == 0) {
                inflater = null;
                if (!loadNextChunk())  {
                    return -1;
                }
                read = inflater.inflate(buf);
            }
            if (read != 1) {
                throw new IOException("Can not decode stream");
            }
            return ((int)buf[0]) &0xff;
        } catch (DataFormatException e) {
            throw new IOException("Can not decode stream " +  e.getMessage());
        }
    }

    private boolean loadNextChunk() throws IOException {
        if (null == inflater) {
            int size = readNextSize();
            if (size < 0) {
                return false;
            }
            byte[] buf = new byte[size];
            int total = 0;
            while (total < size) {
                int read = source.read(buf, total, size - total);
                if (read < 0) {
                    throw new IOException("Can not read next chunk : end of file");
                }
                total += read;
            }
            inflater = new Inflater();
            inflater.setInput(buf);
        }
        return true;
    }

    public void close() throws IOException {
        super.close();
        source.close();
    }
}
